<?php
/**
 * Celebrio Core
 * 
 * @copyright  Copyright (c) 2011 Celebrio Software
 * @package    Celebrio
 * @subpackage Core
 */
namespace Celebrio\Core;

use Nette\Object;

use \SimpleXMLIterator;
use \SimpleXMLElement;

use Celebrio\Core\IModuleLoader;

use Celebrio\Core\ManifestException;

/**
 *
 * @author pavel
 */
class Manifest extends Object {
    
    const I_MODULE_INIT = "Celebrio\\Core\\IModuleInit";
    
    private $xml;
    
    public function __construct($path) {
        $this->xml = new SimpleXMLElement($path, 0, TRUE);
        $namespaces = $this->xml->getDocNamespaces(); 
        $this->xml->registerXPathNamespace('m', $namespaces['']);
    }

    /**
     * Returns module namespace, for example "Celebrio-Database". Since backslash is
     * special (escape) character in PHP, we use dashes as separators as long as we
     * don't need to instantiate any class in the namespace. Use this method when you
     * just want to know the namespace name, group modules by namespace etc.
     *
     * @return string Module namespace separated by DASHES, for example "Celebrio-Database"
     */
    public function getNamespaceDots() {
        $namespace = $this->xml->namespace;
        return $namespace->__toString();
    }

    /**
     * Returns module namespace, for example "Celebrio\\Database". All module classes
     * should be placed in this namespace (or in sub-namespaces of this). The namespace
     * is ready to use in PHP, so it contains doubled backslash as separator. Use this
     * method when you want to directly instantiate module classes. Consider also using
     * getImplementationFullName() and getInterfaceFullName() which already contain
     * namespace in the result.
     *
     * @return string Module namespace, for example "Celebrio\\Database"
     */
    public function getNamespace() {
        $namespace = $this->xml->namespace;
        return $this->dashToBackslash($namespace);
    }

    /**
     * @return string Module identifier, for example "Postgre"
     */
    public function getIdentifier() {
        return $this->xml->identifier->__toString();
    }

    /**
     * @return string Module human-readable name, for example "PostgreSQL database"
     */
    public function getName() {
        return $this->xml->name->__toString();
    }

    /**
     * @return string Module full name in namespace, separated by DASHES. For example "Celebrio-Database-Postgre".
     */
    public function getFullNameDots() {
        return $this->getNamespaceDots() . "-" . $this->getIdentifier();
    }

    /**
     * @return string Module version
     */
    public function getVersion() {
        $version = $this->xml->version;
        return $version->__toString();
    }

    /**
     * @return string Human-readable module description. This usually means 1 - 3 sentences.
     */
    public function getDescription() {
        $description = $this->xml->description->__toString();
        return $description;
    }
    
    public function getDependencies() {
        $dependencies = $this->xml->dependencies;
        return $dependencies;
    }
    
    public function getType() {
        $type = $this->xml->specification->attributes()->type->__toString();
        return $type;
    }
    
    public function isActivable() {
        $type = $this->getType();
        return ManifestType::isActivable($type);
    }
    
    public function isAbstract() {
        $type = $this->getType();
        return $type === "abstract";
    }
    
    public function isImpl() {
        $type = $this->getType();
        return $type === "implementation";
    }

    /**
     * @return string Module interface name, for example IDatabase
     */
    private function getInterface() {
        return $this->xml->specification->interface->__toString();
    }
    
    public function getInterfaceFullName() {
        return $this->itemInNamespace($this->getInterface());
    }
    
    public function getInterfaceFullNameDots() {
        return $this->getNamespaceDots() . "-" . $this->getInterface();
    }
    
    public function getImplementation() {
        return $this->xml->specification->implementation->__toString();
    }
    
    public function getImplementationFullName() {
        $implementation = $this->getImplementation();
        return $this->itemInNamespace($implementation);
    }
    
    public function getService() {
        $service = $this->xml->specification->service->__toString();
        return $service;
    }

    public function isInstalled() {
        return Config::current()->isInstalled($this->getFullNameDots());
    }

    public function isAvailableToInstall() {
        $init = $this->getInit();
        if ($init !== null) {
            return $init->isAvailable() && ManifestType::isInstallable($this->getType());
        } else {
            return false;
        }
    }
    
    /**
     * @return \Celebrio\Core\IModuleInit module initializing class
     */
    public function getInit() {
        $init = $this->xml->specification->init;
        if (!empty($init)) {
            $init = $this->itemInNamespace($init);
            //TODO overit, ze trida existuje
            $initClass = new $init();
            if ( ! $this->classImplements($initClass, self::I_MODULE_INIT)) {
                throw new ManifestException("The init class " . $init . " does not implement " . self::I_MODULE_INIT);
            }
            return $initClass;
        } else {
            return null;
        }
    }

    /**
     * @param $string
     * @return string
     */
    private function dashToBackslash($string) {
        return \str_replace("-", "\\", $string);
    }
    
    private function itemInNamespace($string) {
        return $this->getNamespace() . "\\" . $string;
    }
        
    /**
     * @param $class
     * @param $interface
     * @return bool
     */
    private function classImplements($class, $interface) {
        $interfaces = \class_implements($class);
        if ($interfaces === false) {
            return false;
        } else {
            return \in_array($interface, $interfaces);
        }
    }
}

